<cfscript>
component extends="Item" implements="ItemInterface" {

	function init(required sdb, required string domain, required string itemName) {
		variables.data = {};
		variables.changedKeys = [];
		variables.fetchedAt = _getNeverFetchedDate();
		variables.itemName = itemName;
		return super.init(sdb, domain, itemName);
	}
	
	package function withAttributes(array attrs) {
		variables.data = _toData(attrs);
		variables.fetchedAt = Now();
		return this;
	}
		
	function get(required string key) {
		_fetchInitially();
		return variables.data[key];
	}
		
	function set(required string key, required value) {
		_fetchInitially();
		variables.data[key] = value;
		_addChangedKey(key);
	}
		
	function has(required string key) {
		_fetchInitially();
		return StructKeyExists(variables.data, key);
	}
		
	function remove(string key) {
		_fetchInitially();
		local.attrs = [];
		loop array="#_toAttributes(key, false)#" index="local.i" {
			ArrayAppend(local.attrs, local.i);
		}
		_removeAttributes(local.attrs);
		StructDelete(variables.data, key, false);
		ArrayDelete(variables.changedKeys, key);
	}
	
	function save() {
		_fetchInitially();
		if (_isDirty()) {
			local.attrs = [];
			loop array="#variables.changedKeys#" index="local.key" {
				loop array="#_toAttributes(local.key, true)#" index="local.i" {
					ArrayAppend(local.attrs, local.i);
				}
			}
			_putAttributes(local.attrs);	
			variables.changedKeys = [];
		}
	}
	
	function delete() {
		super.delete()
		variables.changedKeys = [];
		variables.data = {};
	}

	
	function refresh(boolean consistent=false) {
		variables.data = fetch(consistent);
		variables.changedKeys = [];		
		variables.fetchedAt = Now();
	}
	
	function toMemento() {
		_fetchInitially();
		local.result = super.toMemento(variables.data);
		local.result.dirty = _isDirty();
		local.result.fetched_at = variables.fetchedAt;
		return local.result;
	}
	
				
	/* private functions */
	
	private function _toSdbKey(key) {
		return key.trim().toUppercase();
	}
	
	
	private function _addChangedKey(key) {
		if (!ArrayFind(variables.changedKeys, key)) ArrayAppend(variables.changedKeys, key);
	}
	
	private function _isDirty() {
		return (ArrayLen(variables.changedKeys) gt 0) or (variables.fetchedAt eq _getNeverFetchedDate());
	}
	
	private function _toAttributes(key, boolean replaceable) {
		local.attrs = [];
		local.values = _asArray(get(key));
		loop array="#local.values#" index="local.value" {
			if (replaceable) ArrayAppend(local.attrs, _toReplaceableAttribute(key, local.value, true));
			else ArrayAppend(local.attrs, _toReplaceableAttribute(key, local.value, false));
		}
		return local.attrs;
	}
	
	private function _getNeverFetchedDate() {
		return CreateDateTime(1970,1,1,0,0,0);
	}

	
	private function _fetchInitially() {
		if (variables.fetchedAt eq _getNeverFetchedDate()) refresh();
	}
}
</cfscript>